#include "Circle_H.h"
#include "PointH.h"
#include <conio.h>
#include <iostream>
#include "gotoxyH.h"
#include <stdio.h>
#include <math.h>
#include "Animation_H.h"

void gotoxy(int, int);
void clrscr();

void Circle::init()
{

	//If Adding Data members, Please remember to update the operator assignment "="
    m_Radius = 0;
	m_symb = '*';
	m_dirX = m_dirY = m_sumDirX = m_sumDirY = 0;
    m_prevDirX = m_prevDirY = 0;

}

Circle& Circle::operator=(const Circle& copyObj){ //Again there's a dry copy, no need to implement operator =. it's doing the same thing.  anyway I'm moving dirX,dirY to Shape
	//Checks self copy - if yes nothing to do
	if (this == &copyObj) return *this;
	//copy
	m_Radius = copyObj.m_Radius;
	m_center = copyObj.m_center;
	m_symb = copyObj.m_symb;
	m_dirX = copyObj.m_dirX, m_dirY = copyObj.m_dirY, m_sumDirX = copyObj.m_sumDirX, m_sumDirY = copyObj.m_dirY;
	m_prevDirX = copyObj.m_prevDirX, m_prevDirY = copyObj.m_prevDirY;
	//return existing object 
	return *this;
}

void Circle::getPos(int &x, int &y)
{
	m_center.getPos(x,y);
}

void Circle::setPos(int x, int y)
{
	m_center.setPos(x,y);
}

void Circle::getSize(int &r)
{
	r = m_Radius;
}

void Circle::setSize(int x)
{
	m_Radius = x;
}

void Circle::draw(int drawingOption, Screen * scr, bool splitshape)
{

	int x, y;
	int modFactorX = MAX_SCR_WIDTH, modFactorY = MAX_SCR_LEN;
	double pX, pY;
	char frameCh = getSymbol();
	
	x = m_center.getx();
	y = m_center.gety();

	// Adds to that value the movment axis (if there is no movment, the sumDir = 0)
	x += (int)m_sumDirX;
	y += (int)m_sumDirY;

	if (!drawingOption)
	{
		// for every radius lower then the original
		for (int i=0; i < m_Radius; ++i)
			// print circle with blanks
			for ( int j=0; j <=90; j+= PRECISION)
			{
				pX = (cos( (double)j ) * i);
				pY = (sin( (double)j ) * i);

				print4Points (x, y, pX, pY, modFactorX, modFactorY, scr, splitshape, ' ');
			}
	}

	for( int i = 0; i <= 90; i += PRECISION )
	{
		// Calculates the value that is needed to be added to the original center point
		pX = (cos( (double)i ) * m_Radius);
		pY = (sin( (double)i ) * m_Radius);

		print4Points (x, y, pX, pY, modFactorX, modFactorY, scr, splitshape, frameCh);
	}

}


void Circle::print4Points (double x, double y, double currXAxis, double currYAxis,	int modFactorX, int modFactorY, 
	Screen * scr, bool splitshape, char symb)
{
	int printXLeft, printXRight, printYUpper, printYLower; 
	double xLeft, xRight, yUpper, yLower;

	currXAxis = abs(currXAxis);
	currYAxis = abs(currYAxis);

	
	xLeft = x - currXAxis; // x left value
	printXLeft = (int)(floor (xLeft + 0.5 ));

	
	xRight = x + currXAxis; // x right value
	printXRight = (int)(floor (xRight + 0.5 ));

	
	yUpper = y - currYAxis; // y upper value
	printYUpper = (int)(floor (yUpper + 0.5 ));

	
	yLower = y + currYAxis; //y lower value
	printYLower = (int)(floor (yLower + 0.5 ));

	if (splitshape)
	{
		// MOD Limitation (x/y should be Bigger than Mininum that was set on screen)
		if (printXLeft < MIN_SCREEN_X)
		{
			// Next step, increase sum so it is added to the x value so it would be bigger than the screen values by 1 
			m_sumDirX += (m_sumDirX < MIN_SCREEN_X ? (MAX_SCREEN_X-1) : 0); 
			printXLeft += (MAX_SCREEN_X-1);
		}

		if (printYUpper < MIN_SCREEN_Y)
		{
			m_sumDirY += (m_sumDirY < MIN_SCREEN_Y ? (MAX_SCREEN_Y-1) : 0); 
			printYUpper += (MAX_SCREEN_Y-1);
		}

		// the split of the screen action
		printXLeft  %= modFactorX;
		printXRight %= modFactorX;
		printYUpper %= modFactorY;
		printYLower %= modFactorY;
	}

	//call printPoint to print to screen buffer
	Point::printPoint (Point (printXRight, printYLower), scr, symb);
	Point::printPoint (Point (printXLeft, printYLower), scr, symb);
	Point::printPoint (Point (printXRight, printYUpper), scr, symb);
	Point::printPoint (Point (printXLeft, printYUpper), scr, symb);
}


void Circle::getCirclePoints4(Point &p1, Point &p2, Point &p3, Point &p4)
{
  int posX, posY;
  int r;
  
  this->getPos(posX,posY);
  this->getSize(r);
	
  p1.setPos(posX+r, posY); 
  p2.setPos(posX-r, posY); 
  p3.setPos(posX, posY-r); 
  p4.setPos(posX, posY+r); 
}



bool Circle::isInScreen(int x, int y) // is coord. in screen ?
{
	if( x >=0 && x <= MAX_SCR_WID-1)
		if (y >= 0 && y <= MAX_SCR_LEN-1)
			return true;
		
	return false;
}



bool Circle::isDirectionsResetBorders()
{
  Point pLeft, pRight, pUp, pDown;	


  bool clashed = false;

  int circleX, circleY;
  int radius;

  this->getPos(circleX,circleY);
  this->getSize(radius);
  
  int checking = -radius;
  int x = radius;
  int y = 0;
 
  if (x >= y && !clashed)
  {
    this->getCirclePoints4(pRight, pLeft, pUp, pDown);
 
    if(isDirectionsResetBordersCirclesPerPoint(this, pRight)) clashed = true;
	if(isDirectionsResetBordersCirclesPerPoint(this, pLeft)) clashed = true;
	if(isDirectionsResetBordersCirclesPerPoint(this, pUp)) clashed = true;
	if(isDirectionsResetBordersCirclesPerPoint(this, pDown)) clashed = true;
  }

	if(clashed) return true;

	return false;
}

bool Circle::isDirectionsResetBordersCirclesPerPoint(Circle * c, Point p)
{
    int x,y;
	bool chengeMade = false;
	int dirX, dirY;

	p.getPos(x,y);
	c->getIntegerDirectionExplicitly(dirX,dirY);
	
	if(x+dirX > MAX_SCREEN_X || x+dirX < -1) { 

		c->resetDirectionX();
		dirX = -dirX; 
		chengeMade = true;
	}

	if(y+dirY > MAX_SCREEN_Y || y+dirY < -1) { 
		
		c->resetDirectionY();
		dirY = -dirY;
		chengeMade = true;
	}

   if(chengeMade) return true;

	return false;

}

bool Circle::isContain(Circle * c2)
{
	
	int x,y,radius;
	int xC2,yC2,c2Radius;

	this->getPos(x,y);
	this->getSize(radius);
	c2->getPos(xC2,yC2);
	c2->getSize(c2Radius);

	if((xC2-c2Radius >= x-radius) && (xC2+c2Radius <= x+radius) && (yC2-c2Radius >= y-radius) && (yC2+c2Radius <= y+radius)) return true;

	return false;
}

bool Circle::isOverlap(Circle * c2)
{
	int x,y,radius;
	int xC2,yC2,c2Radius;

	this->getPos(x,y);
	this->getSize(radius);
	c2->getPos(xC2,yC2);
	c2->getSize(c2Radius);

	if (xC2+c2Radius > x-radius && xC2-c2Radius < x+radius && yC2+c2Radius > y-radius &&  yC2-c2Radius < y+radius) return true;

	return false;
}


bool Circle::getHitPoint(Circle * c2, Point &pHit)
{
  int c1CircleX, c1CircleY, c2CircleX, c2CircleY;
  int c1Radius, c2Radius;
  bool hitFlag = false;

  Point c1p1, c1p2, c1p3, c1p4, c1p5, c1p6, c1p7, c1p8, c2p1, c2p2, c2p3, c2p4, c2p5, c2p6, c2p7, c2p8;

  getPos(c1CircleX,c1CircleY);
  getSize(c1Radius);
  c2->getPos(c2CircleX,c2CircleY);
  c2->getSize(c2Radius);

  int checkingC1 = -c1Radius;
  int xC1 = c1Radius;
  int yC1 = 0;
 
  while (xC1 >= yC1 && hitFlag == false)
  {
	  
     get4Points(c1CircleX, c1CircleY, xC1, yC1, c1p1, c1p2, c1p3, c1p4);
	 {
		if(isPointInCircleEnvelop(c1p1,c2))
		{
			pHit = c1p1;
		    hitFlag = true;
		}

		if(isPointInCircleEnvelop(c1p2,c2))
		{
            pHit = c1p2;
			hitFlag = true;
	    }

		if(isPointInCircleEnvelop(c1p3,c2))
		{
			pHit = c1p3;
			hitFlag = true;
	    }

		if(isPointInCircleEnvelop(c1p4,c2))
		{
            pHit = c1p4;
			hitFlag = true;
	    }
	 }

	 if(xC1!=yC1 && hitFlag == false)
	 {
	    get4Points(c1CircleX, c1CircleY, yC1, xC1, c1p1, c1p2, c1p3, c1p4);
		{
	     if(isPointInCircleEnvelop(c1p1,c2))
		 {
			pHit = c1p1;
		    hitFlag = true;
		 }

		 if(isPointInCircleEnvelop(c1p2,c2))
		 {
            pHit = c1p2;
			hitFlag = true;
		 }

		 if(isPointInCircleEnvelop(c1p3,c2))
		 {
			pHit = c1p3;
			hitFlag = true;
		 }

		 if(isPointInCircleEnvelop(c1p4,c2))
		 {
            pHit = c1p4;
			hitFlag = true;
		 }
		}
	 }

	checkingC1 += yC1;
    ++yC1;
    checkingC1 += yC1;
 
    if (checkingC1 >= 0)
    {
      checkingC1 -= xC1;
      --xC1;
      checkingC1 -= xC1;
	}
  }


  if (hitFlag) return true;
  
  return false;
}
		  

bool Circle::isPointInCircleEnvelop(const Point &p1, Circle * c2)
{
	int c2CircleX, c2CircleY;
    int c2Radius;
	bool hitFlag = false;

	Point c2p1, c2p2, c2p3, c2p4;
	
	c2->getPos(c2CircleX,c2CircleY);
    c2->getSize(c2Radius);

	int checkingC2 = -c2Radius;
    int xC2 = c2Radius;
    int yC2 = 0;

	while (xC2 >= yC2 && hitFlag==false)
    {  
    
	  get4Points(c2CircleX, c2CircleY, xC2, yC2, c2p1, c2p2, c2p3, c2p4);
		  if(isPointEqual4Points(p1,c2p1, c2p2, c2p3, c2p4)) hitFlag = true;

	  if(xC2!=yC2 && hitFlag==false)
	  {
	    get4Points(c2CircleX, c2CircleY, yC2, xC2, c2p1, c2p2, c2p3, c2p4);
		    if(isPointEqual4Points(p1,c2p1, c2p2, c2p3, c2p4)) hitFlag = true;
	  }

	  checkingC2 += yC2;
      ++yC2;
      checkingC2 += yC2;
 
      if (checkingC2 >= 0)
      {
        checkingC2 -= xC2;
        --xC2;
        checkingC2 -= xC2;
      }
	}

	if(hitFlag) return true;

	return false;
}


bool Circle::isPointEqual4Points(Point pCheck, Point p1, Point p2, Point p3, Point p4)
{
	if(pCheck==p1) return true;
	if(pCheck==p2) return true;
	if(pCheck==p3) return true;
	if(pCheck==p4) return true;

	return false;
}
	


void Circle::get4Points(int CircleX, int CircleY, int x, int y, Point &p1, Point &p2, Point &p3, Point &p4){

	p1.setPos(CircleX + x, CircleY + y);
	p2.setPos(CircleX - x, CircleY + y);
	p3.setPos(CircleX + x, CircleY - y);
	p4.setPos(CircleX - x, CircleY - y);

}

void Circle::get8Points(int CircleX, int CircleY, int x, int y, Point &p1, Point &p2, Point &p3, Point &p4){

  get4Points(CircleX, CircleY, x, y, p1, p2, p3, p4);
  if (x != y) get4Points(CircleX, CircleY, y, x, p1, p2, p3, p4);

}


bool Circle::isXaxis(Point p)
{
	int x,y;
	double sqrtResult;

	p.getPos(x,y);

	sqrtResult = sqrt (double((m_Radius*m_Radius)/2));

	if(y >= (double)y-sqrtResult && y <= (double)y+sqrtResult) return true;

	return false;
}


bool Circle::isYaxis(Point p)
{
	int x,y;
	double sqrtResult;

	p.getPos(x,y);

	sqrtResult = sqrt (double((m_Radius*m_Radius)/2));

	if(x >= (double)x-sqrtResult && x <= (double)x+sqrtResult) return true;

	return false;
}


bool Circle::isPointIn(int x, int y) 
{
	Point p;
	p.setPos(x,y);
	
	return isPointIn(p);
	
}


bool Circle::isPointIn(const Point& p1) const
{
	int x = p.getx(),
		y = p.gety(),
		r = this->getRadius();

	int dp = ((this->getXCenter() - x) * (this->getXCenter() - x)) + ((this->getYCenter() - y) * (this->getYCenter() - y));

	if (dp <= r*r)
		return true;
	else
		return false;

}

bool Circle::isInsideScreen()
{
	int left , right , down , up;

	left = (int)(m_sumDirX + m_center.getx() - m_Radius);
	up = (int)(m_sumDirY + m_center.gety() - m_Radius);
	right = (int)(m_sumDirX + m_center.getx() + m_Radius);
	down = (int)(m_sumDirY + m_center.gety() + m_Radius);

	if (left < MIN_SCREEN_X ||	up < MIN_SCREEN_Y || right >= MAX_SCREEN_X || down >= MAX_SCREEN_Y )
		return false;
	else
		return true;
}